#include "cesium/buffer.hpp"

namespace Cesium {

static void Buffer_allocate(uint32 bytes, uint32* allocated_bytes, uint8** allocated_mem) {
    *allocated_mem = new uint8[bytes];
    if (*allocated_mem != NULL)
        *allocated_bytes = bytes;
}

static void Buffer_deallocate(uint8* buf) {
    delete[] buf;
}


Buffer::VTable Buffer::defaultVTable(Buffer_allocate, Buffer_deallocate);

Buffer::Buffer()
    : buffer(NULL),
      count(0),
      vtptr(&Buffer::defaultVTable)
{
}

Buffer::Buffer(uint32 cnt)
    : buffer(NULL),
      count(0),
      vtptr(&Buffer::defaultVTable)
{
    resize(cnt);
}

Buffer::Buffer(const Buffer& cpy)
    : buffer(NULL),
      count(0),
      vtptr(&Buffer::defaultVTable)
{
    resize(cpy.size());
    assert(size() == cpy.size());
    memcpy(raw(), cpy.raw(), cpy.size());
}

Buffer::Buffer(VTable* vt)
 : buffer(NULL),
   count(0),
   vtptr(vt)
{
}

Buffer::~Buffer() {
    deallocate(buffer);
    buffer = NULL;
    count = 0;
}

uint8* Buffer::raw() const {
    return buffer;
}

uint32 Buffer::size() const {
    return count;
}

uint8& Buffer::operator[](uint32 idx) {
    assert( idx >= 0 && idx < count );
    return buffer[idx];
}

const uint8& Buffer::operator[](uint32 idx) const {
    assert( idx >= 0 && idx < count );
    return buffer[idx];
}

void Buffer::set(uint8* src, uint32 base, uint32 len) {
    assert(base + len <= count);
    memcpy(raw() + base, src, len);
}

void Buffer::set(uint8* src) {
    set(src, 0, size());
}

uint32 Buffer::resize(uint32 sz) {
    uint32 new_size = 0;
    uint8* new_mem = NULL;

    allocate(sz, &new_size, &new_mem);

    if (new_mem && (new_size >= sz || new_size > count)) {
        uint32 cpy_size = (new_size < count) ? new_size : count;
        if (cpy_size > 0)
            memcpy(new_mem, buffer, cpy_size);
        count = new_size;
        buffer = new_mem;
    }
    else {
        deallocate(new_mem);
    }

    return count;
}

Buffer& Buffer::operator=(const Buffer& cpy) {
    resize(cpy.size());
    assert(size() == cpy.size());
    memcpy(raw(), cpy.raw(), cpy.size());
    return *this;
}

void Buffer::allocate(uint32 bytes, uint32* allocated_bytes, uint8** allocated_mem) {
    assert(vtptr != NULL);
    vtptr->alloc(bytes, allocated_bytes, allocated_mem);
}

void Buffer::deallocate(uint8* buf) {
    assert(vtptr != NULL);
    vtptr->dealloc(buf);
}


static void PinnedBuffer_allocate(uint32 bytes, uint32* allocated_bytes, uint8** allocated_mem) {
    *allocated_bytes = 0;
    *allocated_mem = NULL;
}

static void PinnedBuffer_deallocate(uint8* buf) {
    return;
}

Buffer::VTable PinnedBuffer::defaultVTable(&PinnedBuffer_allocate, &PinnedBuffer_deallocate);

PinnedBuffer::PinnedBuffer(uint8* buf, uint32 len)
 : Buffer(&PinnedBuffer::defaultVTable)
{
    count = len;
    buffer = buf;
}

PinnedBuffer::~PinnedBuffer() {
}



} // namespace Cesium
